{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 异常是什么\n",
    "Python使用异常对象来表示异常状态，并在遇到错误时引发异常。异常对象未被处理（或捕 获）时，程序将终止并显示一条错误消息（traceback）。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "ename": "ZeroDivisionError",
     "evalue": "division by zero",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mZeroDivisionError\u001b[0m                         Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-1-9e1622b385b6>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[1;36m1\u001b[0m\u001b[1;33m/\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;31mZeroDivisionError\u001b[0m: division by zero"
     ]
    }
   ],
   "source": [
    "1/0"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "每个异常都是某个类（这里是ZeroDivisionError）的实例。你能以各种方式引发和捕获这些实例，从而逮住错误并采取措施，而不是放任整个程序失败"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# raise语句\n",
    "要引发异常，可使用raise语句，并将一个类（必须是Exception的子类）或实例作为参数。将类作为参数时，将自动创建一个实例。下面的示例使用的是内置异常类Exception："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "raise Exception"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "raise Exception('hyperdrice overload')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 内置的异常类\n",
    "\n",
    "在“Python库参考手册”的Built-in Exceptions一节，可找到有关所有内置异常类的描述。这些异常类都可用于raise语句中。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 自定义的异常类\n",
    "就像创建其他类一样，但务必直接或间接地继承Exception（这意味着从任何内置异常类派生都可以）。因此，自定义异常类的代码类似于下面这样：\n",
    "\n",
    "       class SomeCustomException(Exception): pass"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 捕获异常\n",
    "异常比较有趣的地方是可对其进行处理，通常称之为捕获异常。为此，可使用 __try/except__ 语句。假设你创建了一个程序，让用户输入两个数，再将它们相除，如下所示："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x = int(input('Enter the first number: ')) \n",
    "y = int(input('Enter the second number: ')) \n",
    "print(x / y)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "上面程序运行正常，直到用户输入的第二个数为零。\n",
    "\n",
    "为捕获这种异常并对错误进行处理（这里只是打印一条对用户更友好的错误消息），可像下面这样重写这个程序："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "try:\n",
    "    x = int(input('Enter the first number: '))\n",
    "    y = int(input('Enter the second number: '))\n",
    "    print(x / y)\n",
    "except ZeroDivisionError:\n",
    "    print(\"The second number can't be zero!\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 不提供参数\n",
    "捕获异常后，如果要重新引发它（即继续向上传播），可调用raise且不提供任何参数（也可显式地提供捕获到的异常）\n",
    "\n",
    "为说明这很有用，来看一个能够“抑制”异常ZeroDivisionError的计算器类。如果启用了这种功能，计算器将打印一条错误消息，而不让异常继续传播。在与用户交互的会话中使用这个计算器时，抑制异常很有用；但在程序内部使用时，引发异常是更佳的选择（此时应关闭“抑制”功能）。下面是这样一个类的代码："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class MuffledCalculator: \n",
    "    muffled = False \n",
    "    \n",
    "    def calc(self, expr): \n",
    "        try: \n",
    "            return eval(expr) \n",
    "        except ZeroDivisionError: \n",
    "            if self.muffled: \n",
    "                print('Division by zero is illegal') \n",
    "            else: \n",
    "                raise"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "下面的示例演示了这个类的用法（包括启用和关闭了抑制功能的情形）："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "calculator = MuffledCalculator() \n",
    "calculator.calc('10 / 2')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "calculator.calc('10 / 0') # 关闭了抑制功能"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "calculator.muffled = True \n",
    "calculator.calc('10 / 0')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如你所见，关闭抑制功能时，捕获了异常ZeroDivisionError，但继续向上传播它。\n",
    "\n",
    "如果无法处理异常，在except子句中使用不带参数的raise通常是不错的选择，但有时你可能想引发别的异常。在这种情况下，导致进入except子句的异常将被作为异常上下文存储起来，并出现在最终的错误消息中，如下所示："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "try: \n",
    "    1/0 \n",
    "except ZeroDivisionError: \n",
    "    raise ValueError"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "你可使用raise ... from ...语句来提供自己的异常上下文，也可使用None来禁用上下文。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "try: \n",
    "    1/0 \n",
    "except ZeroDivisionError: \n",
    "    raise ValueError from None"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 多个except语句\n",
    "由于该程序中的except子句只捕获ZeroDivisionError异常，这种异常将成为漏网之鱼，导致程序终止。为同时捕获这种异常，可在try/except语句中再添加一个except子句。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x = int(input('Enter the first number: ')) \n",
    "y = int(input('Enter the second number: ')) \n",
    "print(x / y)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "try: \n",
    "    x = int(input('Enter the first number: ')) \n",
    "    y = int(input('Enter the second number: ')) \n",
    "    print(x / y) \n",
    "except ZeroDivisionError: \n",
    "    print(\"The second number can't be zero!\") \n",
    "except ValueError: \n",
    "    print(\"That wasn't a number, was it?\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 一箭双雕\n",
    "如果要使用一个except子句捕获多种异常，可在一个元组中指定这些异常，如下所示："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "try: \n",
    "    x = int(input('Enter the first number: ')) \n",
    "    y = int(input('Enter the second number: ')) \n",
    "    print(x / y) \n",
    "except (ZeroDivisionError, TypeError, NameError): \n",
    "    print('Your numbers were bogus ...')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 捕获对象\n",
    "要在except子句中访问异常对象本身，可使用两个而不是一个参数。（请注意，即便是在你捕获多个异常时，也只向except提供了一个参数——一个元组。）需要让程序继续运行并记录错误（可能只是向用户显示）时，这很有用。下面的示例程序打印发生的异常并继续运行："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Enter the first number: 10\n",
      "Enter the second number: 0\n",
      "division by zero\n"
     ]
    }
   ],
   "source": [
    "try: \n",
    "    x = int(input('Enter the first number: ')) \n",
    "    y = int(input('Enter the second number: ')) \n",
    "    print(x / y) \n",
    "except (ZeroDivisionError, TypeError) as e: \n",
    "    print(e)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在这个小程序中，except子句也捕获两种异常，但由于你同时显式地捕获了对象本身，因此可将其打印出来，让用户知道发生了什么情况。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 一网打尽\n",
    "即使程序处理了好几种异常，还是可能有一些漏网之鱼。例如，对于前面执行除法运算的程序，如果用户在提示时不输入任何内容就按回车键，将出现一条错误消息，还有一些相关问题出在什么地方的信息（栈跟踪），如下所示：\n",
    "\n",
    "    Traceback (most recent call last): \n",
    "     ... \n",
    "    ValueError: invalid literal for int() with base 10: ''"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这种异常未被try/except语句捕获，这理所当然，因为你没有预测到这种问题，也没有采取相应的措施。在这些情况下，与其使用并非要捕获这些异常的try/except语句将它们隐藏起来，还不如让程序马上崩溃，因为这样你就知道什么地方出了问题。\n",
    "\n",
    "然而，如果你就是要使用一段代码捕获所有的异常，只需在except语句中不指定任何异常类即可。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Enter the first number: 10\n",
      "Enter the second number: \n",
      "Something wrong happened ...\n"
     ]
    }
   ],
   "source": [
    "try: \n",
    "    x = int(input('Enter the first number: ')) \n",
    "    y = int(input('Enter the second number: ')) \n",
    "    print(x / y) \n",
    "except: \n",
    "    print('Something wrong happened ...')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "像这样捕获所有的异常很危险，在大多数情况下，更好的选择是使用except Exception as e并对异常对象进行检查。这样做将让不是从Exception派生而来的为数不多的异常成为漏网之鱼，其中包括SystemExit和KeyboardInterrupt，因为它们是从BaseException（Exception的超类）派生而来的。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 没有异常时\n",
    "在有些情况下，在没有出现异常时执行一个代码块很有用。为此，可像条件语句和循环一样，给try/except语句添加一个else子句。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "A simple task\n",
      "Ah ... It went as planned.\n"
     ]
    }
   ],
   "source": [
    "try: \n",
    "    print('A simple task') \n",
    "except: \n",
    "    print('What? Something went wrong?') \n",
    "else: \n",
    "    print('Ah ... It went as planned.')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Enter the first number: 1\n",
      "Enter the second number: 0\n",
      "Invalid input. Please try again.\n",
      "Enter the first number: ‘foo’\n",
      "Invalid input. Please try again.\n",
      "Enter the first number: bar\n",
      "Invalid input. Please try again.\n",
      "Enter the first number: 10\n",
      "Enter the second number: 2\n",
      "x / y is 5.0\n"
     ]
    }
   ],
   "source": [
    "while True: \n",
    "    try: \n",
    "        x = int(input('Enter the first number: ')) \n",
    "        y = int(input('Enter the second number: ')) \n",
    "        value = x / y \n",
    "        print('x / y is', value) \n",
    "    except: \n",
    "        print('Invalid input. Please try again.') \n",
    "    else: \n",
    "        break"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "一种更佳的替代方案是使用空的except子句来捕获所有属于Exception（或其子类）的异常。你不能完全确定这将捕获所有的异常，因为try/except语句中的代码可能使用旧式的字符串异常或引发并非从Exception派生而来的异常。然而，如果使用except Exception as e，可在程序中打印更有用的错误消息。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Enter the first number: 1\n",
      "Enter the second number: 0\n",
      "Invalid input: division by zero\n",
      "Please try again\n",
      "Enter the first number: ‘x’\n",
      "Invalid input: invalid literal for int() with base 10: '‘x’'\n",
      "Please try again\n",
      "Enter the first number: ‘y’\n",
      "Invalid input: invalid literal for int() with base 10: '‘y’'\n",
      "Please try again\n",
      "Enter the first number: quuux\n",
      "Invalid input: invalid literal for int() with base 10: 'quuux'\n",
      "Please try again\n",
      "Enter the first number: 10\n",
      "Enter the second number: 2\n",
      "x / y is 5.0\n"
     ]
    }
   ],
   "source": [
    "while True: \n",
    "    try: \n",
    "        x = int(input('Enter the first number: ')) \n",
    "        y = int(input('Enter the second number: ')) \n",
    "        value = x / y \n",
    "        print('x / y is', value) \n",
    "    except Exception as e: \n",
    "        print('Invalid input:', e) \n",
    "        print('Please try again') \n",
    "    else: \n",
    "        break"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### finally子句\n",
    "最后，还有finally子句，可用于在发生异常时执行清理工作。这个子句是与try子句配套的。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Cleaning up ...\n"
     ]
    },
    {
     "ename": "ZeroDivisionError",
     "evalue": "division by zero",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mZeroDivisionError\u001b[0m                         Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-9-52fb30a4d7ab>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[0;32m      1\u001b[0m \u001b[0mx\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;32mNone\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      2\u001b[0m \u001b[1;32mtry\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 3\u001b[1;33m     \u001b[0mx\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;36m1\u001b[0m \u001b[1;33m/\u001b[0m \u001b[1;36m0\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m      4\u001b[0m \u001b[1;32mfinally\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      5\u001b[0m     \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'Cleaning up ...'\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;31mZeroDivisionError\u001b[0m: division by zero"
     ]
    }
   ],
   "source": [
    "x = None \n",
    "try: \n",
    "    x = 1 / 0 \n",
    "finally: \n",
    "    print('Cleaning up ...') \n",
    "    del x"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在上述示例中，不管try子句中发生什么异常，都将执行finally子句。为何在try子句之前初始化x呢？因为如果不这样做，ZeroDivisionError将导致根本没有机会给它赋值，进而导致在finally子句中对其执行del时引发未捕获的异常。\n",
    "\n",
    "运行上面这个程序，它将在执行清理工作后崩溃。\n",
    "\n",
    "可在一条语句中同时包含try、except、finally和else"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 异常和函数\n",
    "异常和函数有着天然的联系。如果不处理函数中引发的异常，它将向上传播到调用函数的地方。如果在那里也未得到处理，异常将继续传播，直至到达主程序（全局作用域）。如果主程序中也没有异常处理程序，程序将终止并显示栈跟踪消息。来看一个示例："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "ename": "Exception",
     "evalue": "Something is wrong",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mException\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-10-9a43a27df79d>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[0;32m     13\u001b[0m         \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'Exception handled'\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m     14\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 15\u001b[1;33m \u001b[0mignore_exception\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;32m<ipython-input-10-9a43a27df79d>\u001b[0m in \u001b[0;36mignore_exception\u001b[1;34m()\u001b[0m\n\u001b[0;32m      4\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      5\u001b[0m \u001b[1;32mdef\u001b[0m \u001b[0mignore_exception\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 6\u001b[1;33m     \u001b[0mfaulty\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m      7\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      8\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;32m<ipython-input-10-9a43a27df79d>\u001b[0m in \u001b[0;36mfaulty\u001b[1;34m()\u001b[0m\n\u001b[0;32m      1\u001b[0m \u001b[1;32mdef\u001b[0m \u001b[0mfaulty\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 2\u001b[1;33m     \u001b[1;32mraise\u001b[0m \u001b[0mException\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'Something is wrong'\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m      3\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      4\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      5\u001b[0m \u001b[1;32mdef\u001b[0m \u001b[0mignore_exception\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;31mException\u001b[0m: Something is wrong"
     ]
    }
   ],
   "source": [
    "def faulty(): \n",
    "    raise Exception('Something is wrong') \n",
    "    \n",
    "    \n",
    "def ignore_exception(): \n",
    "    faulty() \n",
    "    \n",
    "    \n",
    "def handle_exception(): \n",
    "    try: \n",
    "        faulty() \n",
    "    except: \n",
    "        print('Exception handled')\n",
    "\n",
    "ignore_exception()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Exception handled\n"
     ]
    }
   ],
   "source": [
    "handle_exception()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如你所见，faulty中引发的异常依次从faulty和ignore_exception向外传播，最终导致显示一条栈跟踪消息。调用handle_exception时，异常最终传播到handle_exception，并被这里的try/except语句处理。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 条件语句和try/except\n",
    "有时候，可使用条件语句来达成异常处理实现的目标，但这样编写出来的代码可能不那么自然，可读性也没那么高。另一方面，有些任务使if/else完成时看似很自然，但实际上使用try/except来完成要好得多。下面来看两个示例。\n",
    "\n",
    "假设有一个字典，你要在指定的键存在时打印与之相关联的值，否则什么都不做。实现这种功能的代码可能类似于下面这样："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "def describe_person(person): \n",
    "    print('Description of', person['name']) \n",
    "    print('Age:', person['age']) \n",
    "    if 'occupation' in person: \n",
    "        print('Occupation:', person['occupation'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这段代码很直观，但效率不高（虽然这里的重点是代码简洁），因为它必须两次查找'occupation'键：一次检查这个键是否存在（在条件中），另一次获取这个键关联的值，以便将其打印出来。下面是另一种解决方案"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "def describe_person(person): \n",
    "    print('Description of', person['name']) \n",
    "    print('Age:', person['age']) \n",
    "    try: \n",
    "        print('Occupation:', person['occupation']) \n",
    "    except KeyError: pass"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在这里，函数直接假设存在'occupation'键。如果这种假设正确，就能省点事：直接获取并打印值，而无需检查这个键是否存在。如果这个键不存在，将引发KeyError异常，而except子句将捕获这个异常。\n",
    "\n",
    "你可能发现，检查对象是否包含特定的属性时，try/except也很有用。例如，假设你要检查一个对象是否包含属性write，可使用类似于下面的代码："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "try: \n",
    "    obj.write \n",
    "except AttributeError: \n",
    "    print('The object is not writeable') \n",
    "else: \n",
    "    print('The object is writeable')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在很多情况下，相比于使用if/else，使用try/except语句更自然，也更符合Python的风格。因此你应养成尽可能使用try/except语句的习惯."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 不那么异常的情况\n",
    "如果你只想发出警告，指出情况偏离了正轨，可使用模块warnings中的函数warn。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "D:\\Anaconda3\\lib\\site-packages\\ipykernel_launcher.py:2: UserWarning: I've got a bad feeling about this.\n",
      "  \n"
     ]
    }
   ],
   "source": [
    "from warnings import warn \n",
    "warn(\"I've got a bad feeling about this.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "警告只显示一次。如果再次运行最后一行代码，什么事情都不会发生。\n",
    "\n",
    "如果其他代码在使用你的模块，可使用模块warnings中的函数filterwarnings来抑制你发出的警告（或特定类型的警告），并指定要采取的措施，如\"error\"或\"ignore\"。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "ename": "UserWarning",
     "evalue": "Something is very wrong!",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mUserWarning\u001b[0m                               Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-17-cefa335ac6d0>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[0;32m      3\u001b[0m \u001b[0mwarn\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m\"Anyone out there?\"\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      4\u001b[0m \u001b[0mfilterwarnings\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m\"error\"\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 5\u001b[1;33m \u001b[0mwarn\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m\"Something is very wrong!\"\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;31mUserWarning\u001b[0m: Something is very wrong!"
     ]
    }
   ],
   "source": [
    "from warnings import filterwarnings \n",
    "filterwarnings(\"ignore\") \n",
    "warn(\"Anyone out there?\") \n",
    "filterwarnings(\"error\") \n",
    "warn(\"Something is very wrong!\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如你所见，引发的异常为UserWarning。发出警告时，可指定将引发的异常（即警告类别），但必须是Warning的子类。如果将警告转换为错误，将使用你指定的异常。另外，还可根据异常来过滤掉特定类型的警告。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
